mod api;
mod db;
mod models;
mod mqtt;
mod redis;
mod schema;
mod util;
mod cron;

use anyhow::{Result, anyhow};
use dotenv::dotenv;
use rumqttc::v5::AsyncClient;
use std::time::Duration;
use tokio_graceful_shutdown::{SubsystemBuilder, Toplevel};

#[tokio::main]
async fn main() -> Result<()> {
    tracing_subscriber::fmt()
        .with_max_level(tracing::Level::DEBUG)
        .init();

    dotenv().ok();
    let db = db::Db::new(10)?;
    let redis = redis::Redis::new().await?;
    let mqtt_options = mqtt::options()?;
    Toplevel::new(async |s| {
        let (mqtt_client, event_loop) = AsyncClient::new(mqtt_options, 10);
        let db_1 = db.clone();
        let redis_1 = redis.clone();
        let mqtt_client_1 = mqtt_client.clone();
        let db_2 = db.clone();
        let redis_2 = redis.clone();
        let mqtt_client_2 = mqtt_client.clone();
        s.start(SubsystemBuilder::new("api", |a| {
            api::run(a, db_1, redis_1, mqtt_client_1)
        }));
        s.start(SubsystemBuilder::new("mqtt_recv", |a| {
            mqtt::run(a, db_2, redis_2, mqtt_client_2, event_loop)
        }));
        s.start(SubsystemBuilder::new("mqtt_cron", move |a| {
            cron::run(a, db, redis, mqtt_client)
        }));
    })
    .catch_signals()
    .handle_shutdown_requests(Duration::from_secs(30))
    .await
    .map_err(|e| anyhow!(e))
}
